// Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:ffi';
import 'dart:convert';
import 'dart:typed_data';

import 'package:media_kit/ffi/src/allocation.dart';

/// The contents of a native zero-terminated array of UTF-8 code units.
///
/// The Utf8 type itself has no functionality, it's only intended to be used
/// through a `Pointer<Utf8>` representing the entire array. This pointer is
/// the equivalent of a char pointer (`const char*`) in C code.
final class Utf8 extends Opaque {}

/// Extension method for converting a`Pointer<Utf8>` to a [String].
extension Utf8Pointer on Pointer<Utf8> {
  /// The number of UTF-8 code units in this zero-terminated UTF-8 string.
  ///
  /// The UTF-8 code units of the strings are the non-zero code units up to the
  /// first zero code unit.
  int get length {
    _ensureNotNullptr('length');
    final codeUnits = cast<Uint8>();
    return _length(codeUnits);
  }

  /// Converts this UTF-8 encoded string to a Dart string.
  ///
  /// Decodes the UTF-8 code units of this zero-terminated byte array as
  /// Unicode code points and creates a Dart string containing those code
  /// points.
  ///
  /// If [length] is provided, zero-termination is ignored and the result can
  /// contain NUL characters.
  ///
  /// If [length] is not provided, the returned string is the string up til
  /// but not including  the first NUL character.
  String toDartString({int? length}) {
    _ensureNotNullptr('toDartString');
    final codeUnits = cast<Uint8>();
    if (length != null) {
      RangeError.checkNotNegative(length, 'length');
    } else {
      length = _length(codeUnits);
    }

    final bytes = codeUnits.asTypedList(length);
    final result = utf8.decode(bytes, allowMalformed: true);

    // Try to decode bad characters from libmpv.
    try {
      if (result.contains('�')) {
        final characters = result.split('');
        for (int i = 0; i < length; i++) {
          if (characters[i] == '�' && kHTMLCharset.containsKey(bytes[i])) {
            characters[i] = kHTMLCharset[bytes[i]]!;
          }
        }
        return characters.join('');
      }
    } catch (_) {}

    return result;
  }

  static int _length(Pointer<Uint8> codeUnits) {
    var length = 0;
    while (codeUnits[length] != 0) {
      length++;
    }
    return length;
  }

  void _ensureNotNullptr(String operation) {
    if (this == nullptr) {
      throw UnsupportedError(
          "Operation '$operation' not allowed on a 'nullptr'.");
    }
  }
}

/// Extension method for converting a [String] to a `Pointer<Utf8>`.
extension StringUtf8Pointer on String {
  /// Creates a zero-terminated [Utf8] code-unit array from this String.
  ///
  /// If this [String] contains NUL characters, converting it back to a string
  /// using [Utf8Pointer.toDartString] will truncate the result if a length is
  /// not passed.
  ///
  /// Unpaired surrogate code points in this [String] will be encoded as
  /// replacement characters (U+FFFD, encoded as the bytes 0xEF 0xBF 0xBD) in
  /// the UTF-8 encoded result. See [Utf8Encoder] for details on encoding.
  ///
  /// Returns an [allocator]-allocated pointer to the result.
  Pointer<Utf8> toNativeUtf8({Allocator allocator = malloc}) {
    final units = utf8.encode(this);
    final Pointer<Uint8> result = allocator<Uint8>(units.length + 1);
    final Uint8List nativeString = result.asTypedList(units.length + 1);
    nativeString.setAll(0, units);
    nativeString[units.length] = 0;
    return result.cast();
  }
}

/// Decimal to UTF 8 characters.
/// Based on: https://www.utf8-chartable.de/unicode-utf8-table.pl?utf8=oct&unicodeinhtml=dec
const Map<int, String> kHTMLCharset = {
  32: ' ',
  33: '!',
  34: '"',
  35: '#',
  36: '\$',
  37: '%',
  38: '&',
  39: '\'',
  40: '(',
  41: ')',
  42: '*',
  43: '+',
  44: ',',
  45: '-',
  46: '.',
  47: '/',
  48: '0',
  49: '1',
  50: '2',
  51: '3',
  52: '4',
  53: '5',
  54: '6',
  55: '7',
  56: '8',
  57: '9',
  58: ':',
  59: ';',
  60: '<',
  61: '=',
  62: '>',
  63: '?',
  64: '@',
  65: 'A',
  66: 'B',
  67: 'C',
  68: 'D',
  69: 'E',
  70: 'F',
  71: 'G',
  72: 'H',
  73: 'I',
  74: 'J',
  75: 'K',
  76: 'L',
  77: 'M',
  78: 'N',
  79: 'O',
  80: 'P',
  81: 'Q',
  82: 'R',
  83: 'S',
  84: 'T',
  85: 'U',
  86: 'V',
  87: 'W',
  88: 'X',
  89: 'Y',
  90: 'Z',
  91: '[',
  92: '\\',
  93: ']',
  94: '^',
  95: '_',
  96: '`',
  97: 'a',
  98: 'b',
  99: 'c',
  100: 'd',
  101: 'e',
  102: 'f',
  103: 'g',
  104: 'h',
  105: 'i',
  106: 'j',
  107: 'k',
  108: 'l',
  109: 'm',
  110: 'n',
  111: 'o',
  112: 'p',
  113: 'q',
  114: 'r',
  115: 's',
  116: 't',
  117: 'u',
  118: 'v',
  119: 'w',
  120: 'x',
  121: 'y',
  122: 'z',
  123: '{',
  124: '|',
  125: '}',
  126: '~',
  127: ' ',
  128: ' ',
  129: '',
  130: ' ',
  131: ' ',
  132: ' ',
  133: ' ',
  134: ' ',
  135: ' ',
  136: ' ',
  137: ' ',
  138: ' ',
  139: ' ',
  140: ' ',
  141: '',
  142: ' ',
  143: '',
  144: '',
  145: ' ',
  146: ' ',
  147: ' ',
  148: ' ',
  149: ' ',
  150: ' ',
  151: ' ',
  152: ' ',
  153: ' ',
  154: ' ',
  155: ' ',
  156: ' ',
  157: '',
  158: ' ',
  159: ' ',
  160: ' ',
  161: '¡',
  162: '¢',
  163: '£',
  164: '¤',
  165: '¥',
  166: '¦',
  167: '§',
  168: '¨',
  169: '©',
  170: 'ª',
  171: '«',
  172: '¬',
  173: '­',
  174: '®',
  175: '¯',
  176: '°',
  177: '±',
  178: '²',
  179: '³',
  180: '´',
  181: 'µ',
  182: '¶',
  183: '·',
  184: '¸',
  185: '¹',
  186: 'º',
  187: '»',
  188: '¼',
  189: '½',
  190: '¾',
  191: '¿',
  192: 'À',
  193: 'Á',
  194: 'Â',
  195: 'Ã',
  196: 'Ä',
  197: 'Å',
  198: 'Æ',
  199: 'Ç',
  200: 'È',
  201: 'É',
  202: 'Ê',
  203: 'Ë',
  204: 'Ì',
  205: 'Í',
  206: 'Î',
  207: 'Ï',
  208: 'Ð',
  209: 'Ñ',
  210: 'Ò',
  211: 'Ó',
  212: 'Ô',
  213: 'Õ',
  214: 'Ö',
  215: '×',
  216: 'Ø',
  217: 'Ù',
  218: 'Ú',
  219: 'Û',
  220: 'Ü',
  221: 'Ý',
  222: 'Þ',
  223: 'ß',
  224: 'à',
  225: 'á',
  226: 'â',
  227: 'ã',
  228: 'ä',
  229: 'å',
  230: 'æ',
  231: 'ç',
  232: 'è',
  233: 'é',
  234: 'ê',
  235: 'ë',
  236: 'ì',
  237: 'í',
  238: 'î',
  239: 'ï',
  240: 'ð',
  241: 'ñ',
  242: 'ò',
  243: 'ó',
  244: 'ô',
  245: 'õ',
  246: 'ö',
  247: '÷',
  248: 'ø',
  249: 'ù',
  250: 'ú',
  251: 'û',
  252: 'ü',
  253: 'ý',
  254: 'þ',
  255: 'ÿ',
  256: 'Ā',
  // Since uint8_t has a maximum value of 255, I hope libmpv's incorrect encoding will exist only till here.
  // 257: 'ā',
  // 258: 'Ă',
  // 259: 'ă',
  // 260: 'Ą',
  // 261: 'ą',
  // 262: 'Ć',
  // 263: 'ć',
  // 264: 'Ĉ',
  // 265: 'ĉ',
  // 266: 'Ċ',
  // 267: 'ċ',
  // 268: 'Č',
  // 269: 'č',
  // 270: 'Ď',
  // 271: 'ď',
  // 272: 'Đ',
  // 273: 'đ',
  // 274: 'Ē',
  // 275: 'ē',
  // 276: 'Ĕ',
  // 277: 'ĕ',
  // 278: 'Ė',
  // 279: 'ė',
  // 280: 'Ę',
  // 281: 'ę',
  // 282: 'Ě',
  // 283: 'ě',
  // 284: 'Ĝ',
  // 285: 'ĝ',
  // 286: 'Ğ',
  // 287: 'ğ',
  // 288: 'Ġ',
  // 289: 'ġ',
  // 290: 'Ģ',
  // 291: 'ģ',
  // 292: 'Ĥ',
  // 293: 'ĥ',
  // 294: 'Ħ',
  // 295: 'ħ',
  // 296: 'Ĩ',
  // 297: 'ĩ',
  // 298: 'Ī',
  // 299: 'ī',
  // 300: 'Ĭ',
  // 301: 'ĭ',
  // 302: 'Į',
  // 303: 'į',
  // 304: 'İ',
  // 305: 'ı',
  // 306: 'Ĳ',
  // 307: 'ĳ',
  // 308: 'Ĵ',
  // 309: 'ĵ',
  // 310: 'Ķ',
  // 311: 'ķ',
  // 312: 'ĸ',
  // 313: 'Ĺ',
  // 314: 'ĺ',
  // 315: 'Ļ',
  // 316: 'ļ',
  // 317: 'Ľ',
  // 318: 'ľ',
  // 319: 'Ŀ',
  // 320: 'ŀ',
  // 321: 'Ł',
  // 322: 'ł',
  // 323: 'Ń',
  // 324: 'ń',
  // 325: 'Ņ',
  // 326: 'ņ',
  // 327: 'Ň',
  // 328: 'ň',
  // 329: 'ŉ',
  // 330: 'Ŋ',
  // 331: 'ŋ',
  // 332: 'Ō',
  // 333: 'ō',
  // 334: 'Ŏ',
  // 335: 'ŏ',
  // 336: 'Ő',
  // 337: 'ő',
  // 338: 'Œ',
  // 339: 'œ',
  // 340: 'Ŕ',
  // 341: 'ŕ',
  // 342: 'Ŗ',
  // 343: 'ŗ',
  // 344: 'Ř',
  // 345: 'ř',
  // 346: 'Ś',
  // 347: 'ś',
  // 348: 'Ŝ',
  // 349: 'ŝ',
  // 350: 'Ş',
  // 351: 'ş',
  // 352: 'Š',
  // 353: 'š',
  // 354: 'Ţ',
  // 355: 'ţ',
  // 356: 'Ť',
  // 357: 'ť',
  // 358: 'Ŧ',
  // 359: 'ŧ',
  // 360: 'Ũ',
  // 361: 'ũ',
  // 362: 'Ū',
  // 363: 'ū',
  // 364: 'Ŭ',
  // 365: 'ŭ',
  // 366: 'Ů',
  // 367: 'ů',
  // 368: 'Ű',
  // 369: 'ű',
  // 370: 'Ų',
  // 371: 'ų',
  // 372: 'Ŵ',
  // 373: 'ŵ',
  // 374: 'Ŷ',
  // 375: 'ŷ',
  // 376: 'Ÿ',
  // 377: 'Ź',
  // 378: 'ź',
  // 379: 'Ż',
  // 380: 'ż',
  // 381: 'Ž',
  // 382: 'ž',
  // 383: 'ſ',
  // 384: 'ƀ',
  // 385: 'Ɓ',
  // 386: 'Ƃ',
  // 387: 'ƃ',
  // 388: 'Ƅ',
  // 389: 'ƅ',
  // 390: 'Ɔ',
  // 391: 'Ƈ',
  // 392: 'ƈ',
  // 393: 'Ɖ',
  // 394: 'Ɗ',
  // 395: 'Ƌ',
  // 396: 'ƌ',
  // 397: 'ƍ',
  // 398: 'Ǝ',
  // 399: 'Ə',
  // 400: 'Ɛ',
  // 401: 'Ƒ',
  // 402: 'ƒ',
  // 403: 'Ɠ',
  // 404: 'Ɣ',
  // 405: 'ƕ',
  // 406: 'Ɩ',
  // 407: 'Ɨ',
  // 408: 'Ƙ',
  // 409: 'ƙ',
  // 410: 'ƚ',
  // 411: 'ƛ',
  // 412: 'Ɯ',
  // 413: 'Ɲ',
  // 414: 'ƞ',
  // 415: 'Ɵ',
  // 416: 'Ơ',
  // 417: 'ơ',
  // 418: 'Ƣ',
  // 419: 'ƣ',
  // 420: 'Ƥ',
  // 421: 'ƥ',
  // 422: 'Ʀ',
  // 423: 'Ƨ',
  // 424: 'ƨ',
  // 425: 'Ʃ',
  // 426: 'ƪ',
  // 427: 'ƫ',
  // 428: 'Ƭ',
  // 429: 'ƭ',
  // 430: 'Ʈ',
  // 431: 'Ư',
  // 432: 'ư',
  // 433: 'Ʊ',
  // 434: 'Ʋ',
  // 435: 'Ƴ',
  // 436: 'ƴ',
  // 437: 'Ƶ',
  // 438: 'ƶ',
  // 439: 'Ʒ',
  // 440: 'Ƹ',
  // 441: 'ƹ',
  // 442: 'ƺ',
  // 443: 'ƻ',
  // 444: 'Ƽ',
  // 445: 'ƽ',
  // 446: 'ƾ',
  // 447: 'ƿ',
  // 448: 'ǀ',
  // 449: 'ǁ',
  // 450: 'ǂ',
  // 451: 'ǃ',
  // 452: 'Ǆ',
  // 453: 'ǅ',
  // 454: 'ǆ',
  // 455: 'Ǉ',
  // 456: 'ǈ',
  // 457: 'ǉ',
  // 458: 'Ǌ',
  // 459: 'ǋ',
  // 460: 'ǌ',
  // 461: 'Ǎ',
  // 462: 'ǎ',
  // 463: 'Ǐ',
  // 464: 'ǐ',
  // 465: 'Ǒ',
  // 466: 'ǒ',
  // 467: 'Ǔ',
  // 468: 'ǔ',
  // 469: 'Ǖ',
  // 470: 'ǖ',
  // 471: 'Ǘ',
  // 472: 'ǘ',
  // 473: 'Ǚ',
  // 474: 'ǚ',
  // 475: 'Ǜ',
  // 476: 'ǜ',
  // 477: 'ǝ',
  // 478: 'Ǟ',
  // 479: 'ǟ',
  // 480: 'Ǡ',
  // 481: 'ǡ',
  // 482: 'Ǣ',
  // 483: 'ǣ',
  // 484: 'Ǥ',
  // 485: 'ǥ',
  // 486: 'Ǧ',
  // 487: 'ǧ',
  // 488: 'Ǩ',
  // 489: 'ǩ',
  // 490: 'Ǫ',
  // 491: 'ǫ',
  // 492: 'Ǭ',
  // 493: 'ǭ',
  // 494: 'Ǯ',
  // 495: 'ǯ',
  // 496: 'ǰ',
  // 497: 'Ǳ',
  // 498: 'ǲ',
  // 499: 'ǳ',
  // 500: 'Ǵ',
  // 501: 'ǵ',
  // 502: 'Ƕ',
  // 503: 'Ƿ',
  // 504: 'Ǹ',
  // 505: 'ǹ',
  // 506: 'Ǻ',
  // 507: 'ǻ',
  // 508: 'Ǽ',
  // 509: 'ǽ',
  // 510: 'Ǿ',
  // 511: 'ǿ',
  // 512: 'Ȁ',
  // 513: 'ȁ',
  // 514: 'Ȃ',
  // 515: 'ȃ',
  // 516: 'Ȅ',
  // 517: 'ȅ',
  // 518: 'Ȇ',
  // 519: 'ȇ',
  // 520: 'Ȉ',
  // 521: 'ȉ',
  // 522: 'Ȋ',
  // 523: 'ȋ',
  // 524: 'Ȍ',
  // 525: 'ȍ',
  // 526: 'Ȏ',
  // 527: 'ȏ',
  // 528: 'Ȑ',
  // 529: 'ȑ',
  // 530: 'Ȓ',
  // 531: 'ȓ',
  // 532: 'Ȕ',
  // 533: 'ȕ',
  // 534: 'Ȗ',
  // 535: 'ȗ',
  // 536: 'Ș',
  // 537: 'ș',
  // 538: 'Ț',
  // 539: 'ț',
  // 540: 'Ȝ',
  // 541: 'ȝ',
  // 542: 'Ȟ',
  // 543: 'ȟ',
  // 544: 'Ƞ',
  // 545: 'ȡ',
  // 546: 'Ȣ',
  // 547: 'ȣ',
  // 548: 'Ȥ',
  // 549: 'ȥ',
  // 550: 'Ȧ',
  // 551: 'ȧ',
  // 552: 'Ȩ',
  // 553: 'ȩ',
  // 554: 'Ȫ',
  // 555: 'ȫ',
  // 556: 'Ȭ',
  // 557: 'ȭ',
  // 558: 'Ȯ',
  // 559: 'ȯ',
  // 560: 'Ȱ',
  // 561: 'ȱ',
  // 562: 'Ȳ',
  // 563: 'ȳ',
  // 564: 'ȴ',
  // 565: 'ȵ',
  // 566: 'ȶ',
  // 567: 'ȷ',
  // 568: 'ȸ',
  // 569: 'ȹ',
  // 570: 'Ⱥ',
  // 571: 'Ȼ',
  // 572: 'ȼ',
  // 573: 'Ƚ',
  // 574: 'Ⱦ',
  // 575: 'ȿ',
  // 576: 'ɀ',
  // 577: 'Ɂ',
  // 578: 'ɂ',
  // 579: 'Ƀ',
  // 580: 'Ʉ',
  // 581: 'Ʌ',
  // 582: 'Ɇ',
  // 583: 'ɇ',
  // 584: 'Ɉ',
  // 585: 'ɉ',
  // 586: 'Ɋ',
  // 587: 'ɋ',
  // 588: 'Ɍ',
  // 589: 'ɍ',
  // 590: 'Ɏ',
  // 591: 'ɏ',
};
